篇首语:本文由编程笔记#小编为大家整理,主要介绍了AspectJ——切入点语法之thistargetargsif以及逻辑运算相关的知识,希望对你有一定的参考价值。
篇首语:本文由编程笔记#小编为大家整理,主要介绍了AspectJ——切入点语法之thistargetargsif以及逻辑运算相关的知识,希望对你有一定的参考价值。
更多的切入点语法
本节介绍AspectJ中更多的切入点语法,有很多在之前都用过,这里做一个总结。
0.捕获this
引用的是特定类型对象的连接点
AspectJ提供了this
原生切入点来捕获所有的连接点,这些连接点处的this
引用的是一个特定的类型。
我们在Test13
包下做测试,首先业务类Service
如下:
package Test13;
public class Service
public int add(int a, int b)
return a + b;
public double square(double a)
return a * a;
public String upper(String string)
return string.toUpperCase();
其包含三个方法。测试类Main
如下:
package Test13;
public class Main
public static void main(String[] args)
Service service = new Service();
System.out.println("service.add(1, 2) = " + service.add(1, 2));
System.out.println("service.square(6) = " + service.square(6));
System.out.println("service.upper(\\"Gavin\\") = " + service.upper("Gavin"));
接着我们定义切面ThisAspect
,如下:
package Test13;
public aspect ThisAspect
pointcut thisPointcut(Service service): this(service);
before(Service service): thisPointcut(service)
System.out.println(thisJoinPoint);
System.out.println(thisJoinPoint.getSourceLocation());
System.out.println(service);
切面中,我们定义了thisPointcut(Service service)
切入点,该切入点使用this(service)
选择在源代码中this
引用的是一个Service
对象的连接点。在这里例子中,其实也就是所有在Service
类中的连接点。运行结果如下:
通常情况下,this
原生切入点和其他切入点配合使用,使在通知中可以使用当前this
引用的对象。
1.使用target
捕获目标对象是特定类型的连接点
与this
原生切入点类似,target
原生切入点可以捕获所有目标对象是特定类型连接点。如在上例中,我们将切入点的this
变成target
:
package Test13;
public aspect ThisAspect
pointcut thisPointcut(Service service): target(service);
before(Service service): thisPointcut(service)
System.out.println(thisJoinPoint);
System.out.println(thisJoinPoint.getSourceLocation());
System.out.println(service);
此时,就会捕获所有目标对象是Service
类对象的连接点,运行结果如下:
可以看到,多出来了Main
类中的几个对Service
类方法的调用连接点,因为这些连接点的目标对象是Service
类的对象,所以都被捕获到。而其他的原本就在Service
类中的连接点也依然被捕获到,它们的目标对象显然也是Service
类对象。
target
原生切入点通常也与其他切入点结合使用。
2.使用args
捕获具有特定参数类型、参数次序和参数个数的连接点
使用args
可以捕获具有特定参数类型、参数次序、参数个数的连接点。在args
中也可以使用通配符,用以选择通配符指定的具有特定参数个数和次序的连接点。
我们在Test14
包下做测试,业务类Service
和测试类Main
与上例一样,另外我们新建切面ArgsAspect
,如下:
package Test14;
public aspect ArgsAspect
pointcut argsPointcut(): args(*, *);
before(): argsPointcut()
System.out.println(thisJoinPoint);
System.out.println(thisJoinPoint.getSourceLocation());
我们使用args(*, *)
选择具有两个参数的连接点。运行结果如下:
如果我们将args(*, *)
换成args(*)
,那么就只会选择只有一个参数的通配符了:
package Test14;
public aspect ArgsAspect
pointcut argsPointcut(): args(*) && !within(ArgsAspect);
before(): argsPointcut()
System.out.println(thisJoinPoint);
System.out.println(thisJoinPoint.getSourceLocation());
另外,还可以使用args(*, int)
,选择有两个参数,并且第二个参数是int
类型的连接点;args(float, .., int)
选择第一个参数是float
类型,最后一个参数是int
类型的,并且中间有任意个参数的连接点。
通常情况下,args
通常和其他切入点一起使用,来获取连接点的参数,如下用法:
package Test14;
public aspect ArgsAspect
pointcut argsPointcut(int a, int b): call(* add(..)) && args(a, b);
before(int a, int b): argsPointcut(a, b)
System.out.println(thisJoinPoint);
System.out.println(thisJoinPoint.getSourceLocation());
System.out.println("第一个参数是:" + a);
System.out.println("第二个参数是:" + b);
运行结果是:
3.切入点的与或非
运算
切入点之间可以进行与或非
运算,分别对应符号&&
、||
和!
。
与&&
表示同时满足多个切入点表达式的切入点。关于&&
的示例前面见过很多,这里不再赘述。
或||
的意义也很明显,它可以让切入点选择多个不同的连接点。比如,我们将上面的例子修改如下:
public aspect ArgsAspect
pointcut argsPointcut(): call(* add(..)) || call(* square(..));
before(): argsPointcut()
System.out.println(thisJoinPoint);
System.out.println(thisJoinPoint.getSourceLocation());
call(* add(..)) || call(* square(..))
表示捕获对add
方法的调用和对square
方法的调用,运行结果如下:
非!
运算前面也用到过,其作用是取反,比如!withincode(* add(..))
排除在add
方法中的连接点,!within(ArgsAspect)
排除在切面ArgsAspect
中的连接点。另外,非运算也可以用在某一个方法签名的字段前面, 比如我们继续将上例修改如下:
public aspect ArgsAspect
pointcut argsPointcut(): execution(public !static * *(..));
before(): argsPointcut()
System.out.println(thisJoinPoint);
System.out.println(thisJoinPoint.getSourceLocation());
切入点execution(public !static * *(..))
表示所有非静态方法的执行连接点,也就是捕获除了public static void main(String[] args)
方法之外的其他所有方法的执行连接点,运行结果如下:
如果将!static
改为static
,那么此时的运行结果为:
两者的区别显而易见。
4.使用if
来设置连接点上的运行时条件
if
原生切入点可以设置运行时条件,只有在if
判断为true
的时候,才捕获该连接点。语法是:
pointcut [切入点名字](要获取的参数): if(Boolean expression);
其具有两个关键特征:
if(Boolean expression)
切入点评估在运行时提供的变量,得到关于连接点是否应该触发相应通知的true
或false
结果。expression
可以由多个逻辑元素组成,包括展示的连接点环境、静态变量、以及其他切入点声明。
我们在Test15
包下做一个简单的测试(这个测试可能不太符合常理,不过演示出效果即可),首先新建People
类:
package Test15;
public class People
private String name;
private int age;
public People(String name, int age)
this.name = name;
this.age = age;
public String getName()
return name;
public void setName(String name)
this.name = name;
public void drive()
System.out.println(name + "不可以开车...");
其中它有属性name
和age
,另外其具有drive()
开车方法,我们规定只有年龄大于18岁才可以开车,并且默认地认为当前对象不可以开车。所以我们新建切面,来捕获对drive()
方法的执行,并为其织入环绕通知:
package Test15;
public aspect DriveAspect
pointcut drivePointcut():execution(void People.drive())
&& if((thisJoinPoint.getThis() instanceof People)
&& ((People)thisJoinPoint.getThis()).getAge() > 18);
void around():drivePointcut()
System.out.println(((People)thisJoinPoint.getThis()).getName() + "可以开车...");
在切入点drivePointcut
中,我们捕获了对drive()
方法的执行,并且只有在当前this
引用的People
对象的年龄大于18的时候,我们才捕获该切入点,此时在为其织入的环绕通知中,我们改变原来程序的运行逻辑,输出可以开车
。
接着,新建测试类Main
如下:
package Test15;
public class Main
public static void main(String[] args)
People people = new People("Gavin", 17);
people.drive();
首先,我们设置“Gavin”的年龄为17岁,这时的运行结果是:
接着,假如我们设置“Gavin”的年龄为19岁,这时的运行结果为:
两个结果与我们的预期完全一样,说明我们使用if
定义的切入点完全正确。